package com.hitqz.robot.biz.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.RateLimiter;
import com.hitqz.robot.api.business.dto.RobotEquipmentControlButtonDto;
import com.hitqz.robot.api.business.enums.RobotOtherCommandEnum;
import com.hitqz.robot.api.business.util.ExcelUtil;
import com.hitqz.robot.api.common.entity.RobotEquipment;
import com.hitqz.robot.biz.core.FileProperties;
import com.hitqz.robot.camera.EquipmentFactory;
import com.hitqz.robot.camera.IEquipmentService;
import com.hitqz.robot.camera.configure.EquipmentCmdEnum;
import com.hitqz.robot.camera.dto.*;
import com.hitqz.robot.camera.type.PtzCtrlTypeEnum;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.driver.parent.RobotDriverService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;

/**
 * @author xupkun
 * @date 2024/6/15
 */

@RestController
@RequiredArgsConstructor
@RequestMapping("/ctrlEquipment")
@Tag(name = "外设控制")
@Slf4j
public class CtrlEquipmentController {

    private final EquipmentFactory cameraFactory;

    private final FileProperties fileProperties;

    private final RobotDriverService robotDriverService;

    private final RateLimiter limiter = RateLimiter.create(5);

    //记录上次控制参数,用于停止
    private static Long ptzCtrlTimeStamp = 0L;
    private static boolean ptzCtrlFlag;
    private static RobotEquipment lastEquipment;
    private static PtzCtrlDTO lastPtzCtrlDTO;
    private static QueryParamsDto lastQuery;
    @Operation(summary = "按钮控制")
    @PostMapping("/button" )
    public void button(@RequestBody RobotEquipmentControlButtonDto button, HttpServletResponse response) throws IOException {
        if (limiter.tryAcquire(1)){
            log.info("web button: {}", button.getAction());
        }            RobotEquipment robotEquipment = JSON.parseObject((String) button.getParam(), RobotEquipment.class);
        this.handleButton(robotEquipment,button,response);

        ExcelUtil.response2Web(response,"指令下发成功");
    }


    private void handleButton(RobotEquipment robotEquipment, RobotEquipmentControlButtonDto buttonDto,HttpServletResponse response) throws IOException {
        IEquipmentService service = cameraFactory.getServiceByBrand(robotEquipment.getBrand());
        if (service==null){
            return;
        }
        this.buttonSimple(robotEquipment, buttonDto, service);
        this.buttonNeedReturn(robotEquipment, buttonDto,service, response);
        this.buttonNeedStop(robotEquipment, buttonDto,service);
    }

    private void buttonSimple(RobotEquipment robotEquipment, RobotEquipmentControlButtonDto buttonDto,IEquipmentService service){
        PtzCtrlDTO ptzCtrlDTO = new PtzCtrlDTO();
        ptzCtrlDTO.setIp(robotEquipment.getIp());
        ptzCtrlDTO.setChannel(robotEquipment.getChannel());
        ptzCtrlDTO.setSpeed(0);
        QueryParamsDto queryParamsDto = new QueryParamsDto();
        queryParamsDto.setPassword(robotEquipment.getPassword());
        queryParamsDto.setUsername(robotEquipment.getUsername());
        queryParamsDto.setPort(robotEquipment.getPort());
        queryParamsDto.setChannel(robotEquipment.getChannel());
        queryParamsDto.setIp(robotEquipment.getIp());
        switch (buttonDto.getAction()){
            case "lightOn"->{
                ptzCtrlDTO.setFlag(0);
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.LIGHT.getByBrand(robotEquipment.getBrand()));
                //service.ptzCtrl(ptzCtrlDTO);
                service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.PTZ_MOVE, ptzCtrlDTO);
            }
            case "lightOff"->{
                ptzCtrlDTO.setFlag(1);
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.LIGHT.getByBrand(robotEquipment.getBrand()));
                //service.ptzCtrl(ptzCtrlDTO);
                service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.PTZ_MOVE, ptzCtrlDTO);
            }
            case "wiper"->{
                ptzCtrlDTO.setFlag(0);
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.WIPER.getByBrand(robotEquipment.getBrand()));
                //service.ptzCtrl(ptzCtrlDTO);
                service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.PTZ_MOVE, ptzCtrlDTO);
            }
            case "autoFocusMode"->{
                CameraParamDTO cameraParamDTO = new CameraParamDTO();
                cameraParamDTO.setByFocusMode((byte) 2);
                cameraParamDTO.setOnlyChangeMode(true);
                //service.setCameraParam(cameraParamDTO);
                service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.FOCUS_MODE, cameraParamDTO);
            }
            case "manualFocusMode"->{
                CameraParamDTO cameraParamDTO = new CameraParamDTO();
                cameraParamDTO.setByFocusMode((byte) 1);
                cameraParamDTO.setOnlyChangeMode(true);
                //service.setCameraParam(cameraParamDTO);
                service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.FOCUS_MODE, cameraParamDTO);
            }
            case "startRecord"->{
                TakePicParamsDto paramsDto = new TakePicParamsDto();
                paramsDto.setBasePath(fileProperties.getLocal().getBasePath());
                paramsDto.setFolder(File.separator + "snapshot" + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator);
                service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.START_RECORD, paramsDto);
            }
            case "reset"-> service.executeCmd(queryParamsDto, false, EquipmentCmdEnum.RESET, null);
            case "stop" -> service.executeCmd(queryParamsDto , false ,EquipmentCmdEnum.STOP, null);
            case "unstop" -> service.executeCmd(queryParamsDto , false ,EquipmentCmdEnum.UN_STOP, null);
            case "match" -> service.executeCmd(queryParamsDto , false ,EquipmentCmdEnum.START_MATCH, null);
            case "fetOil" -> {
                FetchOilDto fetchOilDto = new FetchOilDto();
                fetchOilDto.setSn(0);
                service.executeCmd(queryParamsDto , true ,EquipmentCmdEnum.FET_OIL, fetchOilDto);
            }
            case "mathExit"-> service.executeCmd(queryParamsDto , true ,EquipmentCmdEnum.MATCH_EXIT, null);
            case "errorReset" -> service.executeCmd(queryParamsDto , true ,EquipmentCmdEnum.ERROR_RESET, null);
            case "sidesWay"-> {
                SidesWayDto sidesWayDto = new SidesWayDto();
                //JSONObject jsonObject = JSONObject.parseObject(String.valueOf(buttonDto.getParam()));
                sidesWayDto.setDistance(-5);
                service.executeCmd(queryParamsDto , true ,EquipmentCmdEnum.ENABLE_SIDES, sidesWayDto);
            }
            case "enableSidesWay"-> service.executeCmd(queryParamsDto , true ,EquipmentCmdEnum.ENABLE_SIDES_WAY, null);
            case "doorCtrl" -> {
                DoorCtrlDto doorCtrlDto = new DoorCtrlDto();
                doorCtrlDto.setDoor(1);
                service.executeCmd(queryParamsDto , true ,EquipmentCmdEnum.PLC_DOOR_CTRL, doorCtrlDto);
            }
            case "closeDoor" -> {
                DoorCtrlDto doorCtrlDto = new DoorCtrlDto();
                doorCtrlDto.setDoor(2);
                service.executeCmd(queryParamsDto , true ,EquipmentCmdEnum.PLC_DOOR_CTRL, doorCtrlDto);
            }
            case "jackingUp"->{
                service.executeCmd(queryParamsDto,true,EquipmentCmdEnum.JACKING_UP,null);
            }
            case "jackingDown"->{
                service.executeCmd(queryParamsDto,true,EquipmentCmdEnum.JACKING_DOWN,null);
            }
        }
    }


    private void buttonNeedStop(RobotEquipment robotEquipment, RobotEquipmentControlButtonDto buttonDto,IEquipmentService service){
        //控制
        PtzCtrlDTO ptzCtrlDTO = new PtzCtrlDTO();
        ptzCtrlDTO.setIp(robotEquipment.getIp());
        ptzCtrlDTO.setChannel(robotEquipment.getChannel());
        ptzCtrlDTO.setSpeed(4);
        ptzCtrlDTO.setFlag(0);
        boolean ctrl = false;
        QueryParamsDto queryParamsDto = new QueryParamsDto();
        queryParamsDto.setPassword(robotEquipment.getPassword());
        queryParamsDto.setUsername(robotEquipment.getUsername());
        queryParamsDto.setPort(robotEquipment.getPort());
        queryParamsDto.setChannel(robotEquipment.getChannel());
        queryParamsDto.setIp(robotEquipment.getIp());
        switch (buttonDto.getAction()){
            case "up"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.TILT_UP.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
            case "down"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.TILT_DOWN.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
            case "left"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.PAN_LEFT.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
            case "right"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.PAN_RIGHT.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
            case "zoomIn"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.ZOOM_IN.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
            case "zoomOut"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.ZOOM_OUT.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
            case "focusNear"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.FOCUS_NEAR.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
            case "focusFar"->{
                ptzCtrlDTO.setCtrlType(PtzCtrlTypeEnum.FOCUS_FAR.getByBrand(robotEquipment.getBrand()));
                ptzCtrlTimeStamp = System.currentTimeMillis();
                ctrl = true;
            }
        }
        if (ctrl && !ptzCtrlFlag) {
            //service.ptzCtrl(ptzCtrlDTO);
            service.executeCmd(queryParamsDto, false, EquipmentCmdEnum.PTZ_MOVE, ptzCtrlDTO);
            lastPtzCtrlDTO =  ptzCtrlDTO;
            lastEquipment = robotEquipment;
            ptzCtrlFlag = true;
        }
    }


    private void buttonNeedReturn(RobotEquipment robotEquipment, RobotEquipmentControlButtonDto buttonDto,IEquipmentService service,HttpServletResponse response ){
        //抓拍
        QueryParamsDto queryParamsDto = new QueryParamsDto();
        queryParamsDto.setPassword(robotEquipment.getPassword());
        queryParamsDto.setUsername(robotEquipment.getUsername());
        queryParamsDto.setPort(robotEquipment.getPort());
        queryParamsDto.setChannel(robotEquipment.getChannel());
        queryParamsDto.setIp(robotEquipment.getIp());
        switch (buttonDto.getAction()){
            case "takePic"->{
                //MediaFileDto mediaFileDto = service.takePic(robotEquipment.getIp(), robotEquipment.getChannel(), fileProperties.getLocal().getBasePath(), File.separator + "snapshot" + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator);
                TakePicParamsDto paramsDto = new TakePicParamsDto();
                paramsDto.setBasePath(fileProperties.getLocal().getBasePath());
                paramsDto.setFolder(File.separator + "snapshot" + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator);
                MediaFileDto mediaFileDto = (MediaFileDto) service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.TAKE_PIC,paramsDto);
                if (mediaFileDto!=null) {
                    response.setContentType("application/octet-stream");
                    response.setCharacterEncoding("utf-8");
                    String fileName = URLEncoder.encode("snapshot", StandardCharsets.UTF_8);
                    response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".jpeg");
                    try {
                        ServletOutputStream outputStream = response.getOutputStream();
                        outputStream.write(FileUtil.readBytes(mediaFileDto.getAbsolutePath()));
                        outputStream.close();
                        log.error("抓拍返回图片");
                    }catch (Exception e){
                        log.error("抓拍到 web 失败",e);
                    }
                }
            }
            case "stopRecord"->{
                TakePicParamsDto paramsDto = new TakePicParamsDto();
                paramsDto.setBasePath(fileProperties.getLocal().getBasePath());
                paramsDto.setFolder(File.separator + "snapshot" + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator);
                MediaFileDto mediaFileDto = (MediaFileDto) service.executeCmd(queryParamsDto, true, EquipmentCmdEnum.STOP_RECORD,paramsDto);
                        //service.stopRecord(robotEquipment.getIp(), robotEquipment.getChannel(),
                // fileProperties.getLocal().getBasePath(),
                // File.separator + "snapshot" + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator);
                if (mediaFileDto!=null) {
                    response.setContentType("application/octet-stream");
                    response.setCharacterEncoding("utf-8");
                    String fileName = URLEncoder.encode("snapshot", StandardCharsets.UTF_8);
                    response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".mp4");
                    try {
                        ServletOutputStream outputStream = response.getOutputStream();
                        outputStream.write(FileUtil.readBytes(mediaFileDto.getAbsolutePath()));
                        outputStream.close();
                    }catch (Exception e){
                        log.info("录像到 web 失败",e);
                    }
                }
            }
        }
    }



    @Scheduled(fixedRate = 300)
    public void checkPtzCtrl() {
        if (ptzCtrlFlag && System.currentTimeMillis()-ptzCtrlTimeStamp>=300) {
            if (lastEquipment == null || lastPtzCtrlDTO == null || lastQuery == null){
                return;
            }
            IEquipmentService service = cameraFactory.getServiceByBrand(lastEquipment.getBrand());
            if (service==null){
                return;
            }
            lastPtzCtrlDTO.setFlag(1);
            //service.ptzCtrl(lastPtzCtrlDTO);
            service.executeCmd(lastQuery, true, EquipmentCmdEnum.PTZ_MOVE, lastPtzCtrlDTO);
            log.info("stop ptz");
            ptzCtrlFlag = false;
            lastPtzCtrlDTO = null;
            lastEquipment = null;
            lastQuery = null;
        }
    }


    @Operation(summary = "急停控制")
    @PostMapping("/stop" )
    public R stop(@RequestParam Integer status) throws IOException {
        String varName = "cmd_button_stop";
        String varType = "UDINT";
        String varValue = String.valueOf(status);
        robotDriverService.executeOtherCommand(RobotOtherCommandEnum.WRITE_VAR,
                "{\"varName\":\"" + varName + "\",\"varType\":\"" + varType + "\",\"varValue\":\"" + varValue + "\"}");

        robotDriverService.executeOtherCommand(RobotOtherCommandEnum.WRITE_VAR,
                "{\"varName\":\"" + varName + "\",\"varType\":\"" + varType + "\",\"varValue\":\"" + varValue + "\"}");

        robotDriverService.executeOtherCommand(RobotOtherCommandEnum.WRITE_VAR,
                "{\"varName\":\"" + varName + "\",\"varType\":\"" + varType + "\",\"varValue\":\"" + varValue + "\"}");


        return R.ok();
    }




}
